# Python 中的继承
from abc import abstractmethod, ABCMeta


# 继承是面向对象编程的一个重要概念。继承允许一个类（子类）继承另一个类（父类）的属性和方法，从而使得子类具有父类的全部功能。
# Python同样支持多继承，即一个类可以继承多个父类。
# 在Python3中，如果定义类没有显示指定父类，则默认继承自 object 类。


# 继承的语法和特性：
# 1. 单继承：一个子类只能有一个父类。
# 2. 多继承：一个子类可以有多个父类。
# 3. 多重继承：一个子类可以同时继承多个父类。
# 4. 覆盖(重写)：子类可以重新定义父类的方法。
# 5. 多态：父类引用指向子类对象时，实际调用的是子类的方法。

# 语法：
# class 子类(父类):
#     子类的方法和属性

# 方法重写：
# 子类可以重新定义父类的方法，这叫做方法重写。
# 当子类的方法和父类的方法同名并且参数列表也相同时，这将导致子类的方法重写。


# super()函数：
# super()函数用于调用父类的方法。
# 子类必须在 __init__() 方法中调用父类的 __init__() 方法，以便为子类对象设置父类的属性。
# 否则，子类将无法访问父类的属性。
# 语法：super().__init__(参数列表)


# 多态：
# 父类引用指向子类对象时，实际调用的是子类的方法。
# 多态的好处：
# 1. 子类可以实现父类没有实现的方法，从而增加了功能。
# 2. 父类引用指向子类对象时，实际调用的是子类的方法，可以做到代码的灵活性和可扩展性。

# 多态实现
# 1. 在父类中定义抽象方法、抽象方法使用 @abstractmethod 装饰器。
# 2. 子类实现父类抽象方法。
# 3. 父类引用指向子类对象时，实际调用的是子类的方法。


# 多重继承：
# 一个子类可以同时继承多个父类。
# 语法：class 子类(父类1, 父类2, 父类3):

# 注意：
# 1. 多重继承时，如果父类有相同的方法，则子类会继承第一个父类的方法。
# 2. 多重继承时，子类的方法调用顺序是从左到右，即先调用左边的父类的方法，再调用右边的父类的方法。
# 3. 多重继承时，子类的方法调用顺序可以用 super() 函数来改变。
# 4. 多重继承时，子类的方法调用顺序可以用 MRO（Method Resolution Order）来查看。



# 例如：
# 定义父类 Animal：
class Animal(metaclass=ABCMeta):
    def __init__(self, name):
        self.name = name

    # 父类方法
    def get_name(self):
        return self.name

    # 父类的方法
    def eat(self):
        print(self.name + " 吃食物")


    # 定义抽象方法(抽象方法无需具体实现)
    @abstractmethod
    def run(self):
        pass

    # 定义父类方法，调用抽象方法
    def action(self):
        # 调用抽象方法 - 具体实现由子类实现
        self.run()


# 定义子类 Dog，继承父类 Animal：
# 子类继承父类使用()双括号来指定继承的父类
class Dog(Animal):
    def __init__(self, name):
        # 子类必须在 __init__() 方法中调用父类的 __init__() 方法，以便为子类对象设置父类的属性。
        super().__init__(name)
        # 同样可以使用父类名直接调用父类的构造方法，但不推荐。
        # Animal.__init__(self, name)

    # 子类 Dog 继承了父类 Animal 的属性和方法，并添加了自己的方法 bark()。
    def bark(self):
        print(self.name + " 汪汪汪")

    # 子类重写父类的方法
    def eat(self):
        print(self.name + " 吃屎")

    # 子类实现父类抽象方法
    def run(self):
        print(self.name + " 跑起来了")

class Cat(Animal):
    def __init__(self, name):
        super().__init__(name)

    def meow(self):
        print(self.name + " 喵喵喵")

    def eat(self):
        print(self.name + " 吃耗子")

    # 子类实现父类抽象方法
    def run(self):
        print(self.name + " 跑起来了")

# 实例化子类对象：
dog = Dog("大黄")
print(f"狗 是不是 动物: {isinstance(dog, Animal)}")
# 访问父类属性
print(dog.name)
# 调用父类的方法：
dog.get_name()
# 调用子类的方法：
dog.bark()
# 调用父类的方法被覆盖了，所以调用的是子类的 eat() 方法。
dog.eat()

# 实例化另一个子类对象：
cat = Cat("野猫")
print(f"猫 是不是 动物: {isinstance(cat, Animal)}")
# 访问父类属性
print(cat.name)
# 调用父类的方法
cat.get_name()
# 调用子类的方法
cat.meow()
# 调用父类的方法被覆盖了，所以调用的是子类的 eat() 方法
cat.eat()

print("------ 多态示例 ------------------------")
# 分别创建两个子类对象
d1 = Dog("田园犬")
c1 = Cat("狸花猫")

# 调用从父类继承过来的方法
# 由于父类的 action() 方法调用的 run() 抽象方法，而抽象方法由子类实现
# 因此，实际调用的是子类示例对象的 run() 方法。这样就实现了多态
d1.action()
c1.action()


print("------ 多重继承示例1 ------------------------")
# 定义父类
# 注意：多重继承时，如果父类有相同的方法，则子类会继承第一个父类的方法。
class Wrapper:

    # 父类方法
    def select(self):
        print("select 字段")

    # 父类方法
    def condition(self):
        print("wrapper where 条件")

# 定义父类
class Query:

    # 父类方法 - 与上面的父类方法同名
    def condition(self):
        print("query where 条件")

# 定义子类，继承父类Query 和 Wrapper
# 注意：这里是先继承 Query，再继承 Wrapper
class QueryWrapper(Query, Wrapper):
    pass

# 创建子类对象
qw = QueryWrapper()
# 可以调用多个父类的方法
qw.select()
# 由于子类集成顺序是 Query, Wrapper，因此调用的是 Query 的 condition() 方法
qw.condition()

print("------ 多重继承示例2 ------------------------")
# 定义父类
class Parent:
    #pass

    def __init__(self):
        print("parent __init__")

    def my_func(self):
        print("我是爸爸")

# 定义子类，继承父类Parent
class Child1(Parent):
    # pass
    def __init__(self):
        super().__init__()
        # Parent.__init__(self)
        print("Child1 __init__")

    # def my_func(self):
    #     print("儿砸1")

# 定义子类，继承父类Parent
class Child2(Parent):
    #pass
    def __init__(self):
        super().__init__()
        # Parent.__init__(self)
        print("Child2 __init__")

    # def my_func(self):
    #     print("儿砸2")

# 定义孙子类，继承父类Child1 和 Child2
class Grandson(Child1, Child2):
    #pass

    def __init__(self):
        # Child1.__init__(self)
        # Child2.__init__(self)
        super().__init__()
        print("Grandson __init__")

    # def my_func(self):
    #     print("孙子")

# 创建孙子类对象
g = Grandson()
# 调用 my_func() 方法
# 注意：这里在调用 my_func() 方法时分几种情况：
# 1. 如果孙子类定义了自己的 my_func() 方法，则调用的是孙子类自己的 my_func() 方法。
# 2. 如果孙子类没有定义自己的 my_func() 方法，则调用的是第一个继承的父类 Child1 的方法。
# 3. 如果第一个继承的父类也没有定义 my_func() 方法，则调用的是第二个继承的父类 Child2 的方法。
# 4. 如果第二个继承的父类也没有定义 my_func() 方法，则调用的是 Parent 类的方法。
# 5. 以此类推，直到找到 my_func() 方法被定义的父类。
# 6. 如果所有的父类都没有定义 my_func() 方法，则调用的是 object 类的方法。
# 7. 如果 object 类也没有定义 my_func() 方法，则会报错。
g.my_func()
print("------ MRO示例 ------")
# 打印 Grandson 类的 MRO（Method Resolution Order）
# 注意：MRO 是一个包含所有父类方法的列表，从左到右，即先调用左边的父类的方法，再调用右边的父类的方法。
# [<class '__main__.Grandson'>, <class '__main__.Child1'>, <class '__main__.Child2'>, <class '__main__.Parent'>, <class 'object'>]
print(Grandson.mro())


# 菱形继承：
# 菱形继承是指一个子类同时继承了两个父类，且这两个父类之间存在一个祖先类。
# 菱形继承时，如果子类构造方法使用了 父类名.__init__() 方法来初始化父类，
# 则会导致子类构造方法调用两次父类构造方法，从而导致父类属性被设置两次。
# 为了避免这种情况，推荐使用 super().__init__() 函数来调用父类构造方法。